<?php
namespace Forxx;
/**
 * 管理多进程
 */
class MmProcess {
	//调试模式
	protected $is_debug = true;
	//子进程的数量
	protected $child_num = 0;
	//子进程最大的数量
	private $max_child_num = 1;
	private $is_started = false;
	private $pid_path = '';
	//是否已成功开启
	private $flag = false;

	//工作模式
	protected $mode = 1;	//1为发送，2为接受

	public function __construct($max_child_num, $mode = 1)
	{
		if (!extension_loaded("pcntl")) {
			exit("pcntl extension is not loaded\n");
		}

		//只允许在cli模式下运行
		if (substr(php_sapi_name(), 0, 3) !== 'cli') {
			exit("This program must run in CLI mode\n");
		}
		$this->max_child_num = $max_child_num;
		$this->mode = !empty($mode) ? intval($mode) : 1;
		$this->pid_path = '/tmp/MmProcess' . $this->mode . '.pid';
		
	}

	public function run()
	{

		if($this->is_debug){
			error_reporting(-1);
		}else{
			error_reporting(0);
		}
		//创建守护进程
		$this->create_dameon();
		//注册信号
		$this->register_signal();
		while(true){
			
			if($this->child_num < $this->max_child_num){
				//创建子进程并执行
				$this->execute();
				//调用每个等待信号通过 pcntl_signal() 安装的处理器
				pcntl_signal_dispatch();
			}else{
				
				pcntl_wait($status, WUNTRACED);
				if(pcntl_wifsignaled($status)){
					$this->execute();
				}
			}
			if($this->child_num == $this->max_child_num && !$this->flag){
				echo "\nMmProcess start successfully.\n";
				$this->flag = true;
			}
			
		}
		//调用每个等待信号通过 pcntl_signal() 安装的处理器
		pcntl_signal_dispatch();
		
	}

	/**
	 * 创建守护进程
	 */
	public function create_dameon()
	{
		$pid = pcntl_fork();
		if($pid == -1){
			exit('could not fork');
		}else if($pid){
			//父进程退出
			file_put_contents($this->pid_path, $pid);
			exit();
		}else{
			//将此子进程变成守护进程
			posix_setsid();
		}
	}

	/**
	 * 注册信号
	 */
	public function register_signal()
	{
		$signals = array(
			SIGINT  => "SIGINT",
			SIGHUP  => "SIGHUP",
			SIGQUIT => "SIGQUIT",
			SIGILL  => "SIGILL",
			SIGCHLD => "SIGCHLD",
			SIGTERM => "SIGTERM",
		);
		foreach ($signals as $signal => $name) {
			if (!pcntl_signal($signal, array($this, "handler"))) {
				exit("Install signal handler for {$name} failed");
			}
		}
	}

	/**
	 * 创建进程并执行相应程序
	 */
	public function execute()
	{

		$pid = pcntl_fork();

		if($pid == -1){
			exit('could not fork');
		}else if($pid){
			//父进程
			$this->child_num ++;
		}else{
			//子进程的业务代码
			while(true){
				if(!($result = $this->work($this->mode))){
					usleep(1);
				}

				if(posix_getppid() == 1){
					$this->child_num --;
					posix_kill(posix_getpid(), SIGKILL);
				}

				if($this->flag && $this->child_num == 0){
					echo "MmProcess has been stopped\n";
				}
			}
			exit();
		}
					
	}

	/**
	 * 根据信号做相应处理
	 * @param  $signo 信号
	 */
	public function handler($signo)
	{	
		switch ($signo) {
			case SIGINT:
			case SIGILL:
			case SIGCHLD:
			case SIGTERM:
			case SIGKILL:
				$this->child_num--;
				$this->execute();
				exit();
				break;
			default :
				break;
		}
	}

	/**
	 * kill all process
	 */
	public function quit()
	{
		if(!file_exists($this->pid_path)){
			return;
		}

		$pid = file_get_contents($this->pid_path);
		unlink($this->pid_path);
		posix_kill($pid, SIGKILL);

	}

	/**
	 * 子进程执行内容
	 */
	public function work()
	{
		echo 1;
		sleep(20);
	}
}

